/*
 * GAQCASA.cpp
 *
 *  Created on: Feb 25, 2013
 *      Author: lfelipe
 */
#include <iostream>
#include <fstream>
#include <sstream>

#include "GAQCASA.h"
#include "../Entity/RandomNumber.h"
#include "../Entity/Instance.h"
#include "../Model/GeneticAlgorithm.h"
#include "../Boundary/InstanceLayerDefault.h"
#include "../Boundary/Config.h"
#include "../Model/EvaluationFunction.h"
#include "../Model/FiEvaluationFunction.h"
#include "../Model/TetaEvaluationFunction.h"
#include "../Model/SimulatedeAnnealing.h"
#include "../Entity/Circuit.h"
#include "../Boundary/LatexOutput.h"
GAQCA_SA::GAQCA_SA()
{
    // TODO Auto-generated constructor stub

}

GAQCA_SA::~GAQCA_SA()
{
    // TODO Auto-generated destructor stub
}

void GAQCA_SA::execute(int argc,char *argv[])
{
    if(argc < 8){
        std::cout << "Faltam argumentos: \n: ./GAQCA numero_de_individuos numero_de_iteracao populacao_crossover populacao_mutacao taxa_de_crossover taxa_de_mutacao semente instancia evaluation_function";
    } else {
        int num_individuos = atoi(argv[1]);
        int num_iteracoes = atoi(argv[2]);
        int populacao_crossover = atoi(argv[3]);
        int populacao_mutacao = atoi(argv[4]);
        float taxa_crossover = atof(argv[5]);
        float taxa_mutacao = atof(argv[6]);
        int semente = atoi(argv[7]);

        RandomNumber::get_instance(semente);

        InstanceLayerDefault instance_reader;
        Instance instance = instance_reader.readInstanceWithLayerDefault(Config::get_instance().get_config_string("INSTANCES_PATH") + argv[8]);

        int evaluation_function = atoi(argv[9]);
        EvaluationFunction * function = NULL;
        switch (evaluation_function) {
        case 1:
            function = new FiEvaluationFunction();
            instance.set_evaluation_funcion(function);
            break;
        case 2:
            function = new TetaEvaluationFunction();
            instance.set_evaluation_funcion(function);
            break;
        default:
            break;
        }

        GeneticAlgorithm algorithm(&instance,num_individuos,num_iteracoes);

        std::cout << instance.get_layer_default()->print_layer() << std::endl;

        algorithm.set_layer_default(instance.get_layer_default());
        algorithm.set_crossover_tax(taxa_crossover);
        algorithm.set_mutation_tax(taxa_mutacao);
        algorithm.set_population_crossover_size(populacao_crossover);
        algorithm.set_population_mutation_size(populacao_mutacao);
        algorithm.set_population_reproduction_size(num_individuos - populacao_crossover - populacao_mutacao);
        algorithm.set_simulation(this);


        std::vector< std::vector<Layer> > individuals_vector;
        std::vector< std::vector<double> > individuals_polarization_error_vector;
        std::vector< std::vector<bool> >  individuals_is_valid_vector;
        std::vector< std::vector<double> > lower_polarization_error_vector;
        std::vector<std::string> simulations_folder;

        for (int seed_i = semente; seed_i < semente+2; ++seed_i) {
            RandomNumber::change_seed(seed_i);
            this->create_folder();
            algorithm.execute();

            std::vector<Layer> individuals = GeneticAlgorithm::convert_best_individuals(algorithm.best_individuals());
            std::vector<double> individuals_polarization_error;
            std::vector<bool> individuals_is_valid;
            std::vector<double> lower_polarization_error;
            for (std::vector<Layer>::iterator it = individuals.begin(); it!=individuals.end(); ++it) {
                //if(it->second.is_valid(instance)){
                SimulatedeAnnealing algorithm;
                Circuit circuito(&*it,instance.truth_table());
                Layer best_layer = algorithm.execute(circuito,instance,100);
                double polarization_error_new =0;
                double polarization_error_actual =0;
                double lower_polarization_error_new = 2;
                double lower_polarization_error_actual = 2;
                bool actual_valid = Individual::is_layer_valid(*it,instance,polarization_error_actual,lower_polarization_error_actual);
                bool new_valid = Individual::is_layer_valid(best_layer,instance,polarization_error_new,lower_polarization_error_new);
                if((actual_valid && new_valid && polarization_error_new < polarization_error_actual) ||
                        (!actual_valid && polarization_error_new < polarization_error_actual)   )
                {
                    *it = best_layer;
                    individuals_polarization_error.push_back(polarization_error_new);
                    individuals_is_valid.push_back(new_valid);
                    lower_polarization_error.push_back(lower_polarization_error_new);
                } else {
                    individuals_polarization_error.push_back(polarization_error_actual);
                    individuals_is_valid.push_back(actual_valid);
                    lower_polarization_error.push_back(lower_polarization_error_actual);
                }


            }
            for (int i  = 0; i < (int)individuals.size(); ++i){
                std::cout << individuals[i].number_of_cells() << "\t" << individuals_polarization_error[i] << "\t"<< lower_polarization_error[i] << "\t"<< individuals_is_valid[i] << std::endl;
            }
            algorithm.clear_execution();
            clear_simulation();
            individuals_vector.push_back(individuals);
            individuals_polarization_error_vector.push_back(individuals_polarization_error);
            individuals_is_valid_vector.push_back(individuals_is_valid);
            lower_polarization_error_vector.push_back(lower_polarization_error);
            simulations_folder.push_back(this->generate_folder_name_sequence());
        }
        this->create_folder();
        save_results(num_iteracoes,individuals_vector,individuals_polarization_error_vector,individuals_is_valid_vector,lower_polarization_error_vector,instance.file_name(),simulations_folder);
        RandomNumber::delete_instance();
        delete instance.evaluation_function();
        Config::delete_instance();
    }
}

typedef struct{
    double average_polarization_error;
    double average_lower_polarization;
    double average_cell_num;
    int count;
} SolutionsType;

void GAQCA_SA::save_results(
        int sa_iterations,
        std::vector<std::vector<Layer> >  &individuals,
        std::vector<std::vector<double> > &individuals_polarization_error,
        std::vector<std::vector<bool> > &individuals_is_valid,
        std::vector<std::vector<double> > & lower_polarization_erros,
        std::string instance_name,
        std::vector<std::string> & simulations_folder)
{
    std::string saida = this->generate_statistics_name_sequence();
    std::ofstream myfile;
    std::cout <<" GRAVANDO: " << saida << std::endl;
    myfile.open (saida.c_str());
    myfile << "****************************" << std::endl;
    myfile << "RESULST\n";
    myfile << "INSTANCE:" << instance_name <<  std::endl << std::endl;
    myfile << "****************************" << std::endl;
    myfile << "SIMULATIONS FOLDER NAMES:" <<  std::endl;

    for (int i = 0; i < (int)simulations_folder.size(); ++i) {
        myfile << simulations_folder[i] <<  std::endl;
    }
    myfile << std::endl;
    myfile << "****************************" << std::endl;
    std::vector<std::map<int, SolutionsType> > pareto_fronts;


    std::vector<std::vector<int> > valid_individuals_index;
    for (int i = 0; i < (int)individuals.size(); ++i) {
        valid_individuals_index.push_back(std::vector<int>());
        for (int j = 0; j < (int)individuals[i].size(); ++j) {
            if(individuals_is_valid[i][j]){
                valid_individuals_index[i].push_back(j);
            }
        }
    }
    //MONTAR O PARETO FRONT PARA CADA SIMULACAO

    std::vector< std::vector<int> > pareto_front_indices_vector;
    for(int i  = 0; i < (int)valid_individuals_index.size(); ++i){

        pareto_front_indices_vector.push_back(std::vector<int>());

        if(valid_individuals_index[i].empty()) continue;
        std::vector<int> pareto_front_indices;
        pareto_front_indices_vector[i].push_back(valid_individuals_index[i][0]);
        // myfile<< individuals[i][*pareto_front_indices_vector[i].rbegin()].number_of_cells() << "\t" << lower_polarization_erros[i][*pareto_front_indices_vector[i].rbegin()] << std::endl;
        //std::cout << individuals[i][*pareto_front_indices_vector[i].rbegin()].number_of_cells() << "\t" << lower_polarization_erros[i][*pareto_front_indices_vector[i].rbegin()] << std::endl;
        for (int individual_i = 1; individual_i < (int)valid_individuals_index[i].size(); individual_i++) {
            if( lower_polarization_erros[i][valid_individuals_index[i][individual_i]] >
            lower_polarization_erros[i][*pareto_front_indices_vector[i].rbegin()] ){
                pareto_front_indices_vector[i].push_back(valid_individuals_index[i][individual_i]);
                // myfile<< individuals[i][*pareto_front_indices_vector[i].rbegin()].number_of_cells() << "\t" << lower_polarization_erros[i][*pareto_front_indices_vector[i].rbegin()] << std::endl;
                // std::cout << individuals[i][*pareto_front_indices_vector[i].rbegin()].number_of_cells() << "\t" << lower_polarization_erros[i][*pareto_front_indices_vector[i].rbegin()] << std::endl;
            }
        }
        // myfile << std::endl;
        // std::cout << std::endl;
    }

    //  std::cout << std::endl;
    //  myfile << std::endl;

    // PEGAR O MAIOR E O MENOR DO FRONT DE TODAS AS SIMULACOES
    double menor_lower_polarization = 2;

    for (int i = 0; i < (int)pareto_front_indices_vector.size(); ++i) {
        for (int j = 0; j < (int)pareto_front_indices_vector[i].size(); ++j) {
            int index = pareto_front_indices_vector[i][j];
            if( lower_polarization_erros[i][index] < menor_lower_polarization) {
                menor_lower_polarization = lower_polarization_erros[i][index];
            }
        }
    }
    // cria a primeira classe do front
    // piso do menor valor
    std::cout << std::endl;
    double primeira_classe_front_pareto =(double)((int)(menor_lower_polarization*10))/10.0;

    std::vector<SolutionsType> average_pareto_front;
    for (double classe = primeira_classe_front_pareto; classe < 1;classe +=  0.05)
    {
        average_pareto_front.push_back(SolutionsType());
        average_pareto_front.rbegin()->average_cell_num = 0;
        average_pareto_front.rbegin()->average_lower_polarization = 0;
        average_pareto_front.rbegin()->average_polarization_error = 0;
        average_pareto_front.rbegin()->count=0;

        for(int i  = 0; i < (int)pareto_front_indices_vector.size(); ++i){
            if(pareto_front_indices_vector[i].empty()) continue;

            int j=0;
            while(j < (int)pareto_front_indices_vector[i].size() && lower_polarization_erros[i][pareto_front_indices_vector[i][j]] < classe){
                ++j;
            }
            if(j < (int)pareto_front_indices_vector[i].size()){
                if(average_pareto_front.rbegin()->average_lower_polarization ==0){
                    average_pareto_front.rbegin()->average_lower_polarization = lower_polarization_erros[i][pareto_front_indices_vector[i][j]];
                    average_pareto_front.rbegin()->average_cell_num = individuals[i][pareto_front_indices_vector[i][j]].number_of_cells();
                    average_pareto_front.rbegin()->average_polarization_error = individuals_polarization_error[i][pareto_front_indices_vector[i][j]];
                } else {
                    average_pareto_front.rbegin()->average_lower_polarization =(average_pareto_front.rbegin()->average_lower_polarization + lower_polarization_erros[i][pareto_front_indices_vector[i][j]])/2.0;
                    average_pareto_front.rbegin()->average_cell_num = (average_pareto_front.rbegin()->average_cell_num + (double)individuals[i][pareto_front_indices_vector[i][j]].number_of_cells())/2.0;
                    average_pareto_front.rbegin()->average_polarization_error = ( average_pareto_front.rbegin()->average_polarization_error+individuals_polarization_error[i][pareto_front_indices_vector[i][j]])/2.0;
                }
                average_pareto_front.rbegin()->count++;
            }

        }
        std::cout << classe << "\t"<< average_pareto_front.rbegin()->average_cell_num << "\t" << average_pareto_front.rbegin()->average_lower_polarization << "\t" << average_pareto_front.rbegin()->average_polarization_error << "\t"<< average_pareto_front.rbegin()->count << std::endl;
        myfile << classe << "\t" << average_pareto_front.rbegin()->average_cell_num << "\t" << average_pareto_front.rbegin()->average_lower_polarization << "\t" << average_pareto_front.rbegin()->average_polarization_error << "\t"<< average_pareto_front.rbegin()->count<< std::endl;
    }

    for (int i = 0; i < individuals.size(); ++i) {
        LatexOutput latex_output;
        std::stringstream oss;
        for (int j = 0; j < individuals[i].size(); ++j) {
            oss << "PARETO_FRONT" << "_" << individuals[i][j].number_of_cells() << "_" << lower_polarization_erros[i][j] << "_VALID:" << individuals_is_valid[i][j];
            latex_output.save_instance_latex(this->generate_tex_name_sequence(oss.str()),individuals[i][j]);
        }

    }


    //
    //    pareto_fronts.push_back(std::map<int, SolutionsType>());
    //    pareto_fronts[i][individuals[i][0].number_of_cells()]
    //                     int best_pareto_index_ = 0;
    //    double best_pareto_lower_polarization = lower_polarization_erros[i][0];
    //    for (int j  = 1; j < (int)individuals[i].size(); ++j){
    //
    //
    //
    //        myfile << individuals[i][j].number_of_cells() << "\t" << individuals_polarization_error[i][j] << "\t"<< lower_polarization_erros[i][j] << "\t" << individuals_is_valid[i][j] << std::endl;
    //        if(pareto_fronts[0].find(individuals[i][j].number_of_cells()) == pareto_fronts[0].end() ){
    //            //                if(individuals_is_valid[i][j]){
    //            SolutionsType solution;
    //            solution.lower_polarization_erro = lower_polarization_erros[i][j];
    //            solution.polarization_error = individuals_polarization_error[i][j];
    //            solution.count = 1;
    //            pareto_fronts[0][individuals[i][j].number_of_cells()] = solution;
    //            //                }
    //        } else {
    //            //                if(individuals_is_valid[i][j]){
    //            pareto_fronts[0][individuals[i][j].number_of_cells()].count++;
    //            pareto_fronts[0][individuals[i][j].number_of_cells()].lower_polarization_erro = (pareto_fronts[0][individuals[i][j].number_of_cells()].lower_polarization_erro + lower_polarization_erros[i][j])/2.0;
    //            pareto_fronts[0][individuals[i][j].number_of_cells()].polarization_error = (pareto_fronts[0][individuals[i][j].number_of_cells()].polarization_error + individuals_polarization_error[i][j])/2.0;
    //            //                }
    //        }
    //    }
    //    myfile << std::endl;
    //}

    //    myfile << "RESULTS :" <<  std::endl;
    //    for(std::map<int, SolutionsType>::iterator it = best_results.begin(); it!= best_results.end();++it) {
    //        myfile << it->first << "\t" << it->second.lower_polarization_erro << "\t" << it->second.polarization_error << "\t" << it->second.count << std::endl;
    //        std::cout << it->first << "\t" << it->second.lower_polarization_erro << "\t" << it->second.polarization_error << "\t" << it->second.count << std::endl;
    //    }
    //
    //    myfile << "PARETO FRONT :" <<  std::endl;


    myfile.close();

}



